#include<vector>
#include<iostream>
#include<sstream>
#include<fstream>
#include<openssl/aes.h>
#include<openssl/rand.h>
#include<stdint.h>
#include<string.h>
#include<stdlib.h>

#include <pbc.h>
#include <glib.h>
#include "celia.h"
#include "common.h"
#include "policy_lang.h"
#include <tr1/unordered_map>

using namespace std;
using namespace std::tr1;

typedef unsigned char byte;

extern const unsigned int AES_KEY_SIZE;
extern const unsigned int AES_BLOCK_BITS;
extern const unsigned int AES_BLOCK_BYTES;
extern const unsigned int AES_KEY_BYTES;

string
randomBytes(unsigned int len);

string marshallByteArray(byte * data, int length);

string
marshallBinary(const string &s);

static unsigned char
getFromHex(const string &hexValues);

string
unmarshallBinary(const string &s);

// ------------------ AES ENCRYPTION ---------------------
AES_KEY *
get_AES_enc_key(const string &key);

AES_KEY * get_default_AES_enc_key();

AES_KEY * get_default_AES_dec_key();

AES_KEY *
get_AES_dec_key(const string &key);

string get_default_OPE_key();

static vector<unsigned char>
getIVec(string salt);

template<typename SIZE_T>
SIZE_T
getBlocks(unsigned int unit, SIZE_T len);

vector<unsigned char>
pad(vector<unsigned char> data, unsigned int unit);

vector<unsigned char>
unpad(vector<unsigned char> data);

void encrypt_AES_CBC(const string &ptext, unsigned char **ctext, int *len, const AES_KEY * enckey, string salt, bool dopad);

string
decrypt_AES_CBC(const string &ctext, const AES_KEY * deckey, string salt, bool dounpad);

string
stringToByteInts(const string &s);


// ----------------------- TYPE MAP --------------------
typedef struct element_tp{
	element_t element;
} element_wrap;

typedef struct element_wrap_pt{
	unordered_map<int,element_wrap> map;
} ptMap;

typedef struct element_wrap_ppt{
	unordered_map<int, ptMap> map;
} pptMap;

struct elementHash{

	inline size_t operator() (element_wrap t) const
	{
	int n= element_length_in_bytes(t.element);
	unsigned char* data = (unsigned char*)malloc(n*sizeof(char));
	element_to_bytes(data,t.element);	
	long val =0;
	for (int i=0; i<n; i++){
		val = 5*val + data[i] + 31;
	}
	return size_t(val);
	}
};


struct equalElement{
	inline bool operator()(element_wrap t1, element_wrap t2) const { 
	if (!element_cmp(t1.element,t2.element))
		return true;
	else 
		return false;
	}
};

typedef struct element_wrap_log{
	unordered_map<element_wrap, int, elementHash, equalElement> map;
} dLogMap;


//-------- DATA STRUCTURE -------------
//maps used for quick encryption and decryption
extern unordered_map<int,dLogMap> owner_discreteLogMap,user_discreteLogMap; //streamID -> (element -> int value) 
extern unordered_map<int,ptMap> owner_plaintextMap,user_plaintextMap; //streamID -> (int value -> element)

//maps for public and transformation keys (used by all parties)
extern unordered_map<int, kpabe_pub_t*> ownerPKMap, cloudPKMap, userPKMap; //(streamID -> pub), (policyID -> pub), (userID -> pub)
extern unordered_map<int, kpabe_prv_t*> cloudTKMap, userTKMap; //(streamID -> TK), (policyID -> TK), (userId-> TK)
extern unordered_map<int, kpabe_msk_t*> ownerMKMap; 

//maps for user deterministc key (X2)
extern unordered_map<int, element_wrap> ownerDetKeyMap, cloudDetKeyMap, userDetKeyMap; //(streamID -> detK) 

//maps for user decryption key (Zu)
extern unordered_map<int, element_wrap> userDecKeyMap; //userId-> decK

//window size map, for owner and user
extern unordered_map<int,int> wsUserMap; //userId -> window sizes

//used as accumulator for aggregate-1
extern unordered_map<int, ptMap> owner_windowKeyMap, owner_randomSumMap, user_currentRSum;//, accumulatorMap; 
//extern unordered_map<int, pptMap> accumulatorMap;
extern unordered_map<int,pptMap> accumulatorMap;

extern unordered_map<int, element_wrap> user_windowKeyMap;//userId -> window Key, streamId -> sum of random values

//used for decrypting Aggregate-3 protocol
//extern unordered_map<int, element_wrap> user_currentRSum; //userId -> current rSum

//-------- METHOD ---------------------
char** parseAttributeUniverse(char** argv, int size);

char** parseEncryptionAttributes(char** argv, int size);

GByteArray* convertToByteArray(unsigned char* cipher, int len);

int toByteArray(GByteArray* buf, unsigned char **byteArray, int *len);
kpabe_pub_t* convertToPubKey(unsigned char* pubBytes, int pubLen);

kpabe_prv_t* convertToPrvKey(kpabe_pub_t* pub, unsigned char* prvBytes, int prvLen);

int convertToElement(kpabe_pub_t* pub, unsigned char* bytes, int len, element_t e);

int toPlaintext_simple(int streamId, element_t val);

int toPlaintext_slidingWindow(int streamId, element_t t, element_t mask);

int compare_element_zn(kpabe_pub_t* pub, unsigned char* e1, unsigned char *e2);
